#include "ThreadCache.h"
#include <iostream>
#include "ConCurrent.h"
#include "ObjectPool.h"

// #include <thread>

// using std::thread;

void testPageCache()
{
  ThreadCache tCache;
  void *ptr1 = tCache.alloc(1);
  void *ptr2 = tCache.alloc(2);
  void *ptr3 = tCache.alloc(8);
  void *ptr4 = tCache.alloc(3);

  tCache.dealloc(ptr1, 8);
  tCache.dealloc(ptr2, 8);
  tCache.dealloc(ptr3, 8);
  tCache.dealloc(ptr4, 8);
}

void testPageCacheDealloc()
{
  void *p1 = ConCurrentAlloc(3);
  void *p2 = ConCurrentAlloc(1);
  void *p3 = ConCurrentAlloc(8);

  ConCurrentDealloc(p1, 8);
  ConCurrentDealloc(p2, 8);
  ConCurrentDealloc(p3, 8);
}

void testConCurrent(int nTimes, int nWorks, int nRounds)
{
  std::vector<std::thread> vThreads;
  for (size_t i{}; i < nWorks; i++)
  {
    vThreads.push_back(
        std::thread([&, nTimes]()
                    {
                      std::vector<void *> vec;
                      vec.reserve(nTimes);

                      for (size_t j{}; j < nRounds; j++)
                      {

                        for (size_t k{}; k < nTimes; k++)
                        {
                          vec.push_back(ConCurrentAlloc((16 * k) % 8192 + 1));
                          // printf("%d\n", k);
                        }

                        for (size_t k{}; k < nTimes; k++)
                        {
                          ConCurrentDealloc(vec[k], 8);
                          // printf("%d\n", j);
                        }

                        vec.clear();
                      } }));
  }

  for (std::thread &th : vThreads)
  {
    th.join();
  }
}

void testBigBatchAlloc()
{
  void *p1 = ConCurrentAlloc(8);
  void *p2 = ConCurrentAlloc(3);
  void *p3 = ConCurrentAlloc(2);

  void *bigP1 = ConCurrentAlloc(257 * 1024);

  ConCurrentDealloc(p1, 1);

  void *bigP2 = ConCurrentAlloc(257 * 1024);
  void *bigP3 = ConCurrentAlloc(257 * 1024);

  ConCurrentDealloc(p2, 1);
  ConCurrentDealloc(p3, 1);

  ConCurrentDealloc(bigP1, 1);
  ConCurrentDealloc(bigP2, 1);
  ConCurrentDealloc(bigP3, 1);

  void *bP1 = ConCurrentAlloc(128 * 1024 * 8);
  void *bP2 = ConCurrentAlloc(129 * 1024 * 8);
  void *bP3 = ConCurrentAlloc(257 * 1024 * 8);
  ConCurrentDealloc(bP1, 1);
  ConCurrentDealloc(bP2, 1);
  ConCurrentDealloc(bP3, 1);

  void *p1024 = ConCurrentAlloc(1024);
  void *p8192 = ConCurrentAlloc(8 * 1024);

  ConCurrentDealloc(p1024, 1);
  ConCurrentDealloc(p8192, 1);
}

void testObjectPool(size_t rounds, size_t times)
{
  ObjectPool<int> pool;
  std::vector<int *> vec;
  vec.reserve(times);
  for (size_t i{}; i < rounds; i++)
  {

    for (size_t j{}; j < times; j++)
    {
      vec.push_back(pool.alloc());
    }
    for (size_t j{}; j < times; j++)
    {
      pool.dealloc(vec[j]);
    }
    vec.clear();
  }
}

void BenchmarkMalloc(size_t ntimes, size_t nworks, size_t rounds)
{
  std::vector<std::thread> vthread(nworks);
  std::atomic<size_t> malloc_costtime{0};
  std::atomic<size_t> free_costtime{0};

  for (size_t k = 0; k < nworks; ++k)
  {
    vthread[k] = std::thread([&, k]()
                             {
			std::vector<void*> v;
			v.reserve(ntimes);

			for (size_t j = 0; j < rounds; ++j)
			{
				size_t begin1 = clock();
				for (size_t i = 0; i < ntimes; i++)
				{
					// v.push_back(malloc(16));
					v.push_back(malloc((16 + i) % 8192 + 1));
				}
				size_t end1 = clock();

				size_t begin2 = clock();
				for (size_t i = 0; i < ntimes; i++)
				{
					free(v[i]);
				}
				size_t end2 = clock();
				v.clear();

				malloc_costtime += (end1 - begin1);
				free_costtime += (end2 - begin2);
			} });
  }

  for (auto &t : vthread)
  {
    t.join();
  }
  printf("%u个线程并发执行%u轮次，每轮次malloc %u次: 花费：",
         nworks, rounds, ntimes);
  cout << (size_t)malloc_costtime / 10000 << " ms" << endl;

  printf("%u个线程并发执行%u轮次，每轮次free %u次: 花费：",
         nworks, rounds, ntimes);
  cout << (size_t)free_costtime / 10000 << " ms" << endl;

  printf("%u个线程并发malloc&free %u次，总计花费：\n",
         nworks, nworks * rounds * ntimes);
  cout << (size_t)(free_costtime + malloc_costtime) / 10000 << " ms" << endl;
}

void testConCurrentAlloc(size_t ntimes, size_t nworks, size_t rounds)
{
  std::vector<std::thread> vthread(nworks);
  std::atomic<size_t> malloc_costtime{0};
  std::atomic<size_t> free_costtime{0};

  for (size_t k = 0; k < nworks; ++k)
  {
    vthread[k] = std::thread([&]()
                             {
			std::vector<void*> v;
			v.reserve(ntimes);

			for (size_t j = 0; j < rounds; ++j)
			{
        // auto begin1 = std::chrono::steady_clock::now();
        auto begin1= clock();
				for (size_t i = 0; i < ntimes; i++)
				{
					// v.push_back(ConCurrentAlloc(16));
					v.push_back(ConCurrentAlloc((16 + i) % 8192 + 1));
				}
        // auto end1 = std::chrono::steady_clock::now();
        auto end1 = clock();

        // auto begin2 = std::chrono::steady_clock::now();
        auto begin2 = clock();
				for (size_t i = 0; i < ntimes; i++)
				{
					ConCurrentDealloc(v[i], 1);
				}
        // auto end2 = std::chrono::steady_clock::now();
        auto end2 = clock();
				v.clear();

				malloc_costtime += (end1 - begin1);
				free_costtime += (end2 - begin2);
			} });
  }

  for (auto &t : vthread)
  {
    t.join();
  }

  printf("%u个线程并发执行%u轮次，每轮次malloc %u次: 花费：",
         nworks, rounds, ntimes);
  cout << (size_t)malloc_costtime / 10000 << " ms" << endl;

  printf("%u个线程并发执行%u轮次，每轮次free %u次: 花费：",
         nworks, rounds, ntimes);
  cout << (size_t)free_costtime / 10000 << " ms" << endl;

  printf("%u个线程并发ConCurrentMalloc&free %u次，总计花费：\n",
         nworks, nworks * rounds * ntimes);
  cout << (size_t)(free_costtime + malloc_costtime) / 10000 << " ms" << endl;
}

void testMalloc(size_t nTimes, size_t nWorks, size_t nRounds)
{
}

int main()
{
  // threadCacheTest();
  // testPageCache();
  // testPageCacheDealloc();
  // testConCurrent(10000, 7, 4);
  // testBigBatchAlloc();

  // testObjectPool(5, 10000);
  testConCurrentAlloc(10000, 5, 10);
  BenchmarkMalloc(10000, 5, 10);

  cout << "success..." << endl;
}
